package org.zebra.web.annotation.sign;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.Ordered;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.zebra.common.exception.ResultException;
import org.zebra.common.exception.StatusCode;

import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 拦截声明了 {@link CheckSign} 注解的方法
 */
@Aspect
@Slf4j
public class CheckSignAspect implements Ordered {

    private CheckSignStrategy checkSignStrategy;

    public CheckSignAspect(CheckSignStrategy checkSignStrategy) {
        this.checkSignStrategy = checkSignStrategy;
    }

    @Before("@annotation(checkSign)")
    public void beforePointCut(JoinPoint joinPoint, CheckSign checkSign) {
        boolean flag = false;
        try {
            ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = Objects.requireNonNull(sra).getRequest();
            Map<String, Object> paramsMap = getParamMap(request);
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            this.setRequestBodyValue(paramsMap, request, method);
            flag = this.checkSignStrategy.check(paramsMap);
        } catch (Exception e) {
            log.error("签名错误", e);
        }
        if (!flag) {
            throw new ResultException(StatusCode.INVALID_SIGNATURE.getCode(), StatusCode.INVALID_SIGNATURE.getMsg());
        }
    }

    private void setRequestBodyValue(Map<String, Object> paramsMap, HttpServletRequest request, Method method)
            throws Exception {
        Annotation[][] annotations = method.getParameterAnnotations();
        if (annotations != null) {
            boolean hasRequestBodyAnnotation = false;
            for (Annotation[] annotation : annotations) {
                for (Annotation a : annotation) {
                    if (a.annotationType() == RequestBody.class) {
                        hasRequestBodyAnnotation = true;
                        break;
                    }
                }
            }
            if (hasRequestBodyAnnotation) {
                String bodyContent = IoUtil.read(request.getInputStream(), "UTF-8");
                if (CharSequenceUtil.isEmpty(bodyContent)) return;
                if (JSONUtil.isTypeJSONArray(bodyContent)) {
                    List result = JSONUtil.toList(bodyContent, List.class);
                    result.forEach(obj -> {
                        paramsMap.putAll((Map) obj);
                    });
                } else {
                    paramsMap.putAll(JSONUtil.toBean(bodyContent, Map.class));
                }
            }
        }
    }

    private Map<String, Object> getParamMap(HttpServletRequest request) {
        Map<String, String[]> parameterMap = request.getParameterMap();
        Map<String, Object> map = new LinkedHashMap(parameterMap.size());
        Iterator var3 = parameterMap.keySet().iterator();
        while (var3.hasNext()) {
            String key = (String) var3.next();
            String[] values = parameterMap.get(key);
            map.put(key, values[0]);
        }
        return map;
    }

    @Override
    public int getOrder() {
        return 2;
    }
}
